package com.amtf.datafile;

import java.util.ArrayList;
import java.util.List;

public class DataRecord {
	private DataRecordDefinition _definition = null;
	private DataField[] _fields = null;
	private List<String> _checkNGMessage = null;

	public DataRecord(DataRecordDefinition definition) {
		this._definition = definition.clone();
		this._checkNGMessage = new ArrayList<String>();

		DataColumnDefinition[] fieldDefs = definition.getColumnDefinitions();
		this._fields = new DataField[fieldDefs.length];
		for (int i = 0; i < this._fields.length; i++) {
			this._fields[i] = new DataField(fieldDefs[i]);
		}
	}

	public static DataRecord parseRecord(DataRecordDefinition definition, String row) throws FormatNotMatchException {
		String[] fieldValues = split(row, definition.getColumnDelimiter(), definition.getSmartQuote(), false);

		int colcount = definition.getColumnDefinitions().length;
		if (colcount != 0 && colcount != fieldValues.length) {
			fieldValues = split(row, definition.getColumnDelimiter(), definition.getSmartQuote(), true);
		}

		return parseRecord(definition, fieldValues);
	}

	public static DataRecord parseRecord(DataRecordDefinition definition, Object[] fieldValues) throws FormatNotMatchException {

		String[] strrecord = new String[fieldValues.length];
		for (int i = 0; i < fieldValues.length; i++) {
			if (fieldValues[i] != null) {
				strrecord[i] = fieldValues[i].toString();
			} else {
				strrecord[i] = "";
			}
		}

		return DataRecord.parseRecord(definition, strrecord);
	}

	public static DataRecord parseRecord(DataRecordDefinition definition, String[] fieldValues) throws FormatNotMatchException {
		DataRecord record = definition.newDataRecordInstance();

		// Whether is row is match or not
		DataColumnDefinition[] columnDefinition = definition.getColumnDefinitions();

		// True:The definition length not equals with the real data
		if (columnDefinition.length != 0 && columnDefinition.length != fieldValues.length) {
			if (record.getFieldCount() > fieldValues.length) {
				for (int i = 0; i < fieldValues.length; i++) {
					record.set(i, fieldValues[i]);
				}
			} else {
				for (int i = 0; i < record.getFieldCount(); i++) {
					record.set(i, fieldValues[i]);
				}
			}

			// String msg = "Column count not match! Actual count:" + fieldValues.length + ". Definition count:" + columnDefinition.length;
			String msg = "エラー内容：項目数が定義と異なります。　データ:" + fieldValues.length + " 定義:" + columnDefinition.length;
			record._checkNGMessage.add(msg);

			if (definition.isColumnCountNGException()) {
				throw new FormatNotMatchException(msg);
			}

			return record;
		} else {
			record.set(fieldValues);
			record.checkFormat();

			return record;
		}
	}

	public boolean checkFormat() throws FormatNotMatchException {
		// Whether is row is match or not
		DataColumnDefinition[] columnDefinition = _definition.getColumnDefinitions();

		// True:The definition length not equals with the real data
		if (columnDefinition.length != 0 && columnDefinition.length != _fields.length) {
			// String msg = "Column count not match! Actual count:" + _fields.length + ". Definition count:" + columnDefinition.length;
			String msg = "エラー内容：項目数が定義と異なります。　データ:" + _fields.length + " 定義:" + columnDefinition.length;
			this._checkNGMessage.add(msg);

			if (_definition.isColumnCountNGException()) {
				throw new FormatNotMatchException(msg);
			}

			return false;
		}

		for (int i = 0; i < _fields.length; i++) {
			DataField field = _fields[i];
			DataColumnDefinition fieldDef = field.getDefinition();
			if (!field.isLengthMatch()) {
				// String msg = "Column(" + (i + 1) + ") Value=\"" + field.getValue() + "\". Field length not match! Actual length:" +
				// field.getValue().length() + " Definition length:" + field.getLength();
				String msg = "列(" + (i + 1) + ") 値=\"" + field.getValue() + "\" エラー内容：項目の桁数が定義と異なります。　データ:" + field.getValue().length() + " 定義:" + fieldDef.getMinLength() + "-"
						+ fieldDef.getMaxLength();

				this._checkNGMessage.add(msg);
				if (fieldDef.isCheckNGException()) {
					throw new FormatNotMatchException(msg);
				}

				continue;
			}

			if (!field.isEmptyMatch()) {
				// String msg = "Column(" + (i + 1) + ") Value=\"" + field.getValue() + "\". Empty value not allowed!" ;
				String msg = "列(" + (i + 1) + ") エラー内容：NotNull制約違反です。";

				this._checkNGMessage.add(msg);
				if (fieldDef.isCheckNGException()) {
					throw new FormatNotMatchException(msg);
				}

				continue;
			}

			if (!field.isTypeMatch()) {
				// String msg = "Column(" + (i + 1) + ") Value=\"" + field.getValue() + "\". Field type not match! Definition type:" +
				// field.getType();

				String typeName = "数字文字";
				if (fieldDef.getType() == DataColumnDefinition.VALUE_TYPE.ALPHABET) {
					typeName = "文字列";
				} else if (fieldDef.getType() == DataColumnDefinition.VALUE_TYPE.NUMERIC) {
					typeName = "数値";
				}

				String msg = "列(" + (i + 1) + ") 値=\"" + field.getValue() + "\" エラー内容：項目の属性が異なります。　定義：" + typeName;

				this._checkNGMessage.add(msg);
				if (fieldDef.isCheckNGException()) {
					throw new FormatNotMatchException(msg);
				}

				continue;
			}
		}

		return this._checkNGMessage.size() == 0;
	}

	public static List<DataRecord> parseRecords(DataRecordDefinition definition, List<Object[]> records) throws FormatNotMatchException {
		DataRecord record = null;
		List<DataRecord> dataRecords = new ArrayList<DataRecord>();

		if (records != null && records.size() > 0) {
			for (Object[] objects : records) {
				record = DataRecord.parseRecord(definition, objects);
				dataRecords.add(record);
			}
		}

		return dataRecords;
	}

	public DataRecordDefinition getDefinition() {
		return _definition;
	}

	public void setDefinition(DataRecordDefinition definition) {
		this._definition = definition;
	}

	@Override
	public DataRecord clone() {
		DataRecord o = new DataRecord(this._definition.clone());
		for (int i = 0; i < this._fields.length; i++) {
			o._fields[i] = this._fields[i].clone();
		}

		for (String msg : this._checkNGMessage) {
			o._checkNGMessage.add(msg);
		}

		return o;
	}

	public static boolean isRecordMatch(DataRecordDefinition definition, String row) {
		try {
			parseRecord(definition, row);
		} catch (FormatNotMatchException e) {
			return false;
		}

		return true;
	}

	public List<String> getFormatNGMessage() {
		return _checkNGMessage;
	}

	public boolean isFormatMatch() {
		return _checkNGMessage.size() == 0;
	}

	public void set(String[] fieldValues) {
		if (_fields.length < fieldValues.length) {
			DataField[] newfields = new DataField[fieldValues.length];

			for (int i = 0; i < _fields.length; i++) {
				newfields[i] = _fields[i];
			}

			DataColumnDefinition def = new DataColumnDefinition(this._definition);
			for (int i = _fields.length; i < fieldValues.length; i++) {
				newfields[i] = new DataField(def);
			}

			_fields = newfields;
		}

		int i = 0;
		for (; i < fieldValues.length; i++) {
			_fields[i].setValue(fieldValues[i]);
		}

		for (; i < _fields.length; i++) {
			_fields[i].setValue("");
		}
	}

	public DataField append(DataField element) {
		DataField[] newfields = new DataField[_fields.length + 1];

		for (int i = 0; i < _fields.length; i++) {
			newfields[i] = _fields[i];
		}

		newfields[_fields.length + 1] = element;
		_fields = newfields;

		return _fields[_fields.length + 1];
	}

	public DataField append(String value, DataColumnDefinition definition) {
		DataField[] newfields = new DataField[_fields.length + 1];

		for (int i = 0; i < _fields.length; i++) {
			newfields[i] = _fields[i];
		}

		newfields[_fields.length + 1] = new DataField(definition).setValue(value);
		_fields = newfields;

		return _fields[_fields.length + 1];
	}

	public DataField insert(int index, DataField element) {
		DataField[] newfields = null;
		if (index >= _fields.length) {
			newfields = new DataField[index + 1];
			for (int i = 0; i < _fields.length; i++) {
				newfields[i] = _fields[i];
			}
		} else {
			newfields = new DataField[_fields.length + 1];
			for (int i = 0; i < index; i++) {
				newfields[i] = _fields[i];
			}
		}

		newfields[index] = element;

		for (int i = index; i < _fields.length; i++) {
			newfields[i + 1] = _fields[i];
		}

		_fields = newfields;
		return _fields[index];
	}

	public DataField insert(int index, String value) {
		DataField[] newfields = null;
		if (index >= _fields.length) {
			newfields = new DataField[index + 1];
			for (int i = 0; i < _fields.length; i++) {
				newfields[i] = _fields[i];
			}
		} else {
			newfields = new DataField[_fields.length + 1];
			for (int i = 0; i < index; i++) {
				newfields[i] = _fields[i];
			}
		}

		DataColumnDefinition def = new DataColumnDefinition(this._definition);
		newfields[index] = new DataField(def);
		newfields[index].setValue(value);

		for (int i = index; i < _fields.length; i++) {
			newfields[i + 1] = _fields[i];
		}

		_fields = newfields;
		return _fields[index];
	}

	public DataField set(String name, DataField element) {
		int i = getFieldIndex(name);
		if (i != -1) {
			_fields[i] = element;
			return _fields[i];
		}

		return null;
	}

	public DataField set(String name, String value) {
		DataField df = this.get(name);
		if (df != null) {
			df.setValue(value);
			return df;
		}

		return null;
	}

	public DataField set(int index, DataField element) {
		_fields[index] = element;
		return _fields[index];
	}

	public DataField set(int index, String value) {
		if (_fields[index] == null) {
			_fields[index] = new DataField(_definition.getDefaultColumnDefinition());
		}

		_fields[index].setValue(value);
		return _fields[index];
	}

	public int getFieldCount() {
		return _fields.length;
	}

	public DataField[] getFields() {
		return _fields;
	}

	public String[] getFieldsValue() {
		String[] values = new String[_fields.length];
		for (int i = 0; i < values.length; i++) {
			values[i] = _fields[i].getValue();
		}
		return values;
	}

	public String[] getFieldsName() {
		String[] values = new String[_fields.length];
		for (int i = 0; i < values.length; i++) {
			values[i] = _fields[i].getDefinition().getName();
		}
		return values;
	}

	public DataField get(int index) {
		return _fields[index];
	}

	public DataField get(String name) {
		int i = getFieldIndex(name);
		if (i != -1) {
			return _fields[i];
		}

		return null;
	}

	public int getFieldIndex(String name) {
		for (int i = 0; i < _fields.length; i++) {
			if (name.equalsIgnoreCase(_fields[i].getName())) {
				return i;
			}
		}

		return -1;
	}

	public static String toValue(DataField[] valFields, DataRecordDefinition definition) {
		if (valFields.length == 0) {
			return "";
		}

		StringBuffer content = new StringBuffer();

		int max = 0;
		char delimiter = definition.getColumnDelimiter();
		// DataColumnDefinition[] tofields = definition.getColumnDefinitions();
		// if (tofields.length == 0) {
		// max = valFields.length - 1;
		// } else {
		// max = valFields.length < tofields.length ? valFields.length - 1 : tofields.length - 1;
		max = valFields.length - 1;
		// }

		for (int i = 0; i < max; i++) {
			content.append(valFields[i].getValue());
			content.append(delimiter);
		}

		content.append(valFields[max].getValue());

		return content.toString();
	}

	public static String toString(DataField[] valFields, DataRecordDefinition definition) {
		if (valFields.length == 0) {
			return "";
		}

		StringBuffer content = new StringBuffer();
		char delimiter = definition.getColumnDelimiter();
		DataColumnDefinition[] tofieldDefs = definition.getColumnDefinitions();
		if (tofieldDefs.length == 0) {
			DataColumnDefinition colDef = new DataColumnDefinition(definition);
			DataField templateField = new DataField(colDef);
			int colSize = valFields.length - 1;
			for (int i = 0; i < colSize; i++) {
				content.append(templateField.setValue(valFields[i].getValue()).toString());
				content.append(delimiter);
			}

			content.append(templateField.setValue(valFields[colSize].getValue()).toString());
		} else {
			int colSize = valFields.length < tofieldDefs.length ? valFields.length - 1 : tofieldDefs.length - 1;
			for (int i = 0; i < colSize; i++) {
				content.append(new DataField(tofieldDefs[i]).setValue(valFields[i].getValue()).toString());
				content.append(delimiter);
			}

			content.append(new DataField(tofieldDefs[colSize]).setValue(valFields[colSize].getValue()).toString());
		}

		return content.toString();
	}

	public String toValue() {
		StringBuffer content = new StringBuffer();

		if (_fields.length > 0) {
			char delimiter = _definition.getColumnDelimiter();

			int max = _fields.length - 1;
			for (int i = 0; i < max; i++) {
				if (_fields[i] != null) {
					content.append(_fields[i].getValue());
				} else {
					content.append("");
				}
				content.append(delimiter);
			}

			if (_fields[max] != null) {
				content.append(_fields[max].getValue());
			} else {
				content.append("");
			}
		}

		return content.toString();
	}

	public String toString() {
		StringBuffer content = new StringBuffer();

		if (_fields.length > 0) {
			char delimiter = _definition.getColumnDelimiter();

			int max = _fields.length - 1;
			for (int i = 0; i < max; i++) {
				if (_fields[i] != null) {
					content.append(_fields[i].toString());
				} else {
					content.append("");
				}
				content.append(delimiter);
			}

			if (_fields[max] != null) {
				content.append(_fields[max].toString());
			} else {
				content.append("");
			}
		}

		return content.toString();
	}

	public static String[] split(String row, char delimiter, char quote, boolean groupQuote) throws FormatNotMatchException {
		int beginIndex, endIndex;
		List<String> strs = new ArrayList<String>();

		if (quote != ' ') {
			int length = row.length();
			beginIndex = 0;
			int[] indexs = null;
			do {
				indexs = indexOfField(row, length, beginIndex, delimiter, quote, groupQuote);
				strs.add(row.substring(indexs[0], indexs[1]));
				beginIndex = indexs[2];
			} while (beginIndex < length);

			if (row.charAt(length - 1) == delimiter) {
				strs.add("");
			}
		} else {
			beginIndex = 0;
			endIndex = row.indexOf(delimiter);
			while (endIndex != -1) {
				strs.add(row.substring(beginIndex, endIndex));
				beginIndex = endIndex + 1;
				endIndex = row.indexOf(delimiter, beginIndex);
			}
			strs.add(row.substring(beginIndex, row.length()));
		}

		String[] result = new String[strs.size()];
		strs.toArray(result);
		return result;
	}

	// private static int[] indexOfField(String row, int length, int beginIndex, char delimiter, char quote) throws FormatNotMatchException {
	// int[] indexs = new int[3];
	// int endIndex = 0;
	// int index = beginIndex;
	// int j;
	// char c;
	// // get the first not empty char
	// while (true) {
	// c = row.charAt(index);
	// if (c == ' ') {
	// if (++index >= length) {
	// indexs[0] = beginIndex;
	// indexs[1] = length;
	// indexs[2] = length;
	// return indexs;
	// }
	//
	// continue;
	// } else {
	// break;
	// }
	// }
	//
	// if (c == delimiter) {
	// indexs[0] = beginIndex;
	// indexs[1] = index;
	// indexs[2] = index + 1;
	// } else if (c == quote) {
	// int quoteOffset = index;
	// int quoteEndIndex = 0;
	// boolean isfieldEnd = false;
	// while (!isfieldEnd) {
	// quoteEndIndex = row.indexOf(quote, quoteOffset + 1);
	// // not found
	// if (quoteEndIndex == -1) {
	// quoteEndIndex = length - 1;
	// }
	//
	// j = quoteEndIndex;
	// // get the first not empty char
	// while (true) {
	// if (++j >= length) {
	// indexs[0] = index;
	// indexs[1] = quoteEndIndex + 1;
	// indexs[2] = length;
	// return indexs;
	// }
	//
	// c = row.charAt(j);
	// if (c == ' ') {
	// continue;
	// } else {
	// break;
	// }
	// }
	//
	// if (c == delimiter) {
	// isfieldEnd = true;
	// indexs[0] = index;
	// indexs[1] = quoteEndIndex + 1;
	// indexs[2] = j + 1;
	// } else {
	// quoteOffset = j - 1;
	// }
	// }
	// } else {
	// endIndex = row.indexOf(delimiter, index + 1);
	// if (endIndex != -1) {
	// indexs[0] = beginIndex;
	// indexs[1] = endIndex;
	// indexs[2] = endIndex + 1;
	// } else {
	// indexs[0] = beginIndex;
	// indexs[1] = length;
	// indexs[2] = length;
	// }
	// }
	//
	// return indexs;
	// }

	private static int[] indexOfField(String row, int length, int beginIndex, char delimiter, char quote, boolean groupQuote) throws FormatNotMatchException {
		int[] indexs = new int[3];
		int endIndex = 0;
		int index = beginIndex;
		int j;
		char c;
		// get the first not empty char
		while (true) {
			c = row.charAt(index);
			if (c == ' ') {
				if (++index >= length) {
					indexs[0] = beginIndex;
					indexs[1] = length;
					indexs[2] = length;
					return indexs;
				}

				continue;
			} else {
				break;
			}
		}

		if (c == delimiter) {
			indexs[0] = beginIndex;
			indexs[1] = index;
			indexs[2] = index + 1;
		} else if (c == quote) {
			if (groupQuote) {
				int quoteOffset = index;
				int quoteEndIndex = 0;
				int floor = 1;
				boolean isfieldEnd = false;
				while (!isfieldEnd) {
					do {
						quoteEndIndex = row.indexOf(quote, quoteOffset + 1);
						// not found
						if (quoteEndIndex == -1) {
							quoteEndIndex = length - 1;
							break;
						}
						//
						quoteOffset = quoteEndIndex;
					} while (--floor != 0);

					j = quoteEndIndex;
					// get the first not empty char
					while (true) {
						if (++j >= length) {
							indexs[0] = index;
							indexs[1] = quoteEndIndex + 1;
							indexs[2] = length;
							return indexs;
						}

						c = row.charAt(j);
						if (c == ' ') {
							continue;
						} else {
							break;
						}
					}

					if (c == delimiter) {
						isfieldEnd = true;
						indexs[0] = index;
						indexs[1] = quoteEndIndex + 1;
						indexs[2] = j + 1;
					} else {
						floor = 2;
						quoteOffset = j - 1;
					}
				}
			} else {
				int quoteOffset = index;
				int quoteEndIndex = 0;
				boolean isfieldEnd = false;
				while (!isfieldEnd) {
					quoteEndIndex = row.indexOf(quote, quoteOffset + 1);
					// not found
					if (quoteEndIndex == -1) {
						quoteEndIndex = length - 1;
					}

					j = quoteEndIndex;
					// get the first not empty char
					while (true) {
						if (++j >= length) {
							indexs[0] = index;
							indexs[1] = quoteEndIndex + 1;
							indexs[2] = length;
							return indexs;
						}

						c = row.charAt(j);
						if (c == ' ') {
							continue;
						} else {
							break;
						}
					}

					if (c == delimiter) {
						isfieldEnd = true;
						indexs[0] = index;
						indexs[1] = quoteEndIndex + 1;
						indexs[2] = j + 1;
					} else {
						quoteOffset = j - 1;
					}
				}
			}
		} else {
			endIndex = row.indexOf(delimiter, index + 1);
			if (endIndex != -1) {
				indexs[0] = beginIndex;
				indexs[1] = endIndex;
				indexs[2] = endIndex + 1;
			} else {
				indexs[0] = beginIndex;
				indexs[1] = length;
				indexs[2] = length;
			}
		}

		return indexs;
	}

	public static void main(String[] s) {
		String x = "\"\", 0, \",\",  \"テスト\"\",\",999 \",  \"66\"\"9\" ,  \",海）欧州,\"：TME_Jun\" ,  7000\"  ,\"201,206\",4.33 , , \"W93441\",, \"99,\"     ,\" 韓 \",\"\",\"99,0";
		// String x = "9,\"DSP D22 12.1\"\",RES TCH,LED,WHT\",99";
		// String x =
		// "\"FTL\",\"12488060\",\"201211\",\"11020127\",\"Platform-國瑞汽車GALC延命保守專用PART採購\",\"Platform国瑞自動車Part購入\",\"Platform Kuozui Motors Part Purchase\",\"SA0111130008001\",\"SSDSC2CW120A3K5 盒裝Intel SSD520系列,120GB,MLC,SATA3(6.0),2.5\" 9.5mm,25奈米\",\"PC部品(SSDSC2CW120A3K5 Intel SSD520 120GB MLC SATA3(6.0) 2.5\"\" 9.5mm)\",\"PC Parts(SSDSC2CW120A3K5 Intel SSD520 120GB MLC SATA3(6.0) 2.5\"\" 9.5mm)\",3110000.00,466609.00000,109.00,\"NTD\",1,\"H/W\",\"FJ Server Inventory\",\"-\",\"-\"";
		// String x =
		// "'', 0, ',',  'テスト,',999 ',  '66'9' ,  ',海）欧州,'：TME_Jun' ,  7000'  ,'201,206',4.33 , , 'W93441',, '99,'     ,' 韓 ',''";
		// String x =
		// "1100,F2,111102155,90,SGD,2011/7/25,4,2011,111102155,SG001692,2011/7/25,10000000,Abacus International Pte Ltd,,,0111102155,0111102155,1,EA,EA,1,4500,SGD,SV3100112231122004,'.5 SDM, .5 Exchange admin, 1.5 Desktop',399990,SV3100112231122004,1130,,1130,SRA-100016-E04,0000059497,9,,1101,10,07,056,Enterprise Acct 1,1003,6111074153,90,ORP,2010/6/30,SG001692,937308.00,SGD,Desktop Outsourcing,,,ZD16,EA,54000,SGD,54000,SGD,10001843,0,0,,0.00,1,4500,10000000";
		// String x = "200388792,\"globalKeiyakuKeitai,\"03";
		// String x = ", 0303 韓, ,";
		// String x = "cc,\"\",,";
		// x = "FJ50324916Gケイラインロジスティックス（株）50324916ケイラインロジスティックス\"K\" LINE LOGISTICS, LTD.0115-0000211-0002693-000010";

		String[] ss = null;
		try {
			ss = split(x, ',', '"', true);
			// ss = split(x, ',', '\'');
		} catch (FormatNotMatchException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(ss);
	}
}
